#ifndef CVarSystem_h__
#define CVarSystem_h__

#include "../infLib/infLib.h"

typedef int cvarFlags_t;
enum cvarFlags_e {
    CVAR_ALL            = -1,
    CVAR_COMMON         = BIT(0),
    CVAR_FILESYSTEM     = BIT(1),
    CVAR_RENDERSYSTEM   = BIT(2)
};

class CVar {
    friend class CVarSystem;
public:
    CVar(const char* name, const char* defaultValue, const char* description, cvarFlags_t flags );
    CVar(const char* name, const char* defaultValue, const char* description, float min, float max, cvarFlags_t flags );
    ~CVar() { }

    INF_INLINE const char*  GetName() const { return name; }

    INF_INLINE void        ResetToDefault() {
        value = defaultValue;
    }

    INF_INLINE void        SetFlags( cvarFlags_t flags ) {
        this->flags = flags;
    }
    cvarFlags_t GetFlags() const {
        return flags;
    }

    INF_INLINE void        SetString(const char* string) {
        value = string;
    }
    INF_INLINE const char* GetString() const {
        return value;
    }

    INF_INLINE void        SetBool(bool b) {
        value = (b ? "1" : "0");
    }
    INF_INLINE bool        GetBool() const {
        return (value == "0") ? false : true;
    }

    INF_INLINE void        SetInteger(int i) {
        value = inf::Str(i);
    }
    INF_INLINE int         GetInteger() const {
        return value.to_integer();
    }

    INF_INLINE void        SetFloat(float f) {
        value = inf::Str(f);
    }
    INF_INLINE float       GetFloat() const {
        return value.to_float();
    }

    static void			    RegisterStaticVars( void );
private:
    CVar& operator = ( const CVar& rhs );	// disable

    void Init(const char* name, const char* defaultValue, cvarFlags_t flags, const char *desc, float min, float max );

    const char*         name;
    inf::Str            value;
    inf::Str            defaultValue;
    float               min;
    float               max;
    const char*         description;
    cvarFlags_t         flags;

    CVar*               next;

    static CVar*        staticLink;
};

INF_INLINE CVar::CVar(const char* name, const char* defaultValue, const char* description, cvarFlags_t flags) {
    Init(name, defaultValue, flags, description, 0.0F, 0.0F);
}
INF_INLINE CVar::CVar(const char* name, const char* defaultValue, const char* description, float min, float max, cvarFlags_t flags) {
    Init(name, defaultValue, flags, description, min, max);
}

class CVarSystem {
public:
	CVarSystem();
	~CVarSystem();

	void			Init();
	void            LinkCVars( CVar* cvars );
	void			Shutdown();

    void            Register( CVar* cvar );

	bool            ResetCVar( const char* name );
    void            ResetFlaggedVariables( cvarFlags_t flags, cvarFlags_t ignoreFlags = 0 );

	bool        CheckExist( const char* name ) const;
    CVar*       FindCVar( const char* name ) const;

    void        SetString( const char* name, const char* value, cvarFlags_t flags =0 );
	void        SetBool( const char* name, bool b, cvarFlags_t flags =0 );
	void        SetInteger( const char* name, int i, cvarFlags_t flags =0 );
	void        SetFloat( const char* name, float f, cvarFlags_t flags =0 );

    const char* GetString( const char* name ) const;
    bool        GetBool( const char* name ) const;
    int         GetInteger( const char* name ) const;
	float       GetFloat( const char* name ) const;

private:
	void        Clear();

	typedef inf::List<CVar*> CVarsList;
	CVarsList    cvarsList;
};

extern CVarSystem* cvarSystem;

INF_INLINE void CVar::Init( const char* name, const char* defaultValue, cvarFlags_t flags, const char *desc, float min, float max )
{
    this->name = name;
    this->value = defaultValue;
    this->defaultValue = defaultValue;
    this->min = min;
    this->max = max;
    this->flags = flags;
    this->description = desc;

    this->next = this;
    if( staticLink != (CVar *)0xFFFFFFFF ) {
        this->next = staticLink;
        staticLink = this;
    } else {
        cvarSystem->Register( this );
    }
}

INF_INLINE void CVar::RegisterStaticVars( void ) {
    if ( staticLink != (CVar *)0xFFFFFFFF ) {
        for ( CVar *cvar = staticLink; cvar; cvar = cvar->next ) {
            cvarSystem->Register( cvar );
        }
        staticLink = (CVar *)0xFFFFFFFF;
    }
}

#endif // CVarSystem_h__
